package org.gwtEss.table;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

public abstract class ClientSideTable<VO> extends OnDemandTable<VO>{

	
	
	private List<VO> dataList, filteredList;

	public ClientSideTable(Cols<VO> cols, int pxWidth, int pxHeight,
			int rowsPerPage) {
		super(cols, /*pxWidth, pxHeight, */rowsPerPage);
		// TODO Auto-generated constructor stub
	}

	@Override
	public void render(int totalRows) {
		throw new RuntimeException("Use render(List<VO>) instead");
	}
	
	public void render(List<VO> dataList){
		this.dataList = dataList;
		super.render(dataList.size());
	}
	private class FilterItemWithOccurance extends FilterMenuItem{
		private int occurances;
		private String displayTxt; 
		public FilterItemWithOccurance(String value,
				String displayTxt, boolean checked, int occurances) {
//			super(value, displayTxt + " (" + occurances + ")", checked);
			this.displayTxt = displayTxt;
			this.occurances = occurances;
			setValue(value);
			setChecked(checked);
			setDisplayTxt();
		}

		public int getOccurances() {
			return occurances;
		}
		private void setDisplayTxt() {
			super.setDisplayTxt(displayTxt + " (" + occurances + ")");
		}
		public void increment() {
			this.occurances++;
			setDisplayTxt();
		} 
		
	}
	@Override
	public void fetchColumnUniqueValues(final ColWidget col,
			FetchCompleteCallBack<FilterMenuItem> filterListCallBack) {
		boolean isComparable = col instanceof HasComparable;
//		if (!(isDisplayTxt || isComparable)) return;
		if (!isComparable) return;//Comparable value always represent the real value of the element.
//		List<FilterMenuItem> filter;
//		if (filterMenus != null && (filter = filterMenus.get(col)) != null)
//			filterListCallBack.onFetchComplete(filter);
//		else {
			LinkedHashMap<String, FilterItemWithOccurance> map = new LinkedHashMap<String, FilterItemWithOccurance>();
			List<VO> data = filteredList ==  null?dataList:filteredList;
			int dataListSize = data.size();
			String displayTxt, comparableValue; 
			boolean isDisplayTxt = col instanceof HasDisplayText;
			for (int i=0; i < dataListSize ; i++){
				comparableValue = ((HasComparable)col).getComparableValue(data.get(i)).toString();
				if (isDisplayTxt)
					displayTxt = ((HasDisplayText)col).createDisplayText(data.get(i)).getText();
				else 
					displayTxt = comparableValue;
				
				FilterItemWithOccurance displayTxtWithOccurance = map.get(displayTxt);
				if (displayTxtWithOccurance == null)
					displayTxtWithOccurance = new FilterItemWithOccurance(comparableValue, displayTxt, true, 1);
				else 
					displayTxtWithOccurance.increment();
				map.put(displayTxt, displayTxtWithOccurance);
				/*if (col instanceof SystemManagedColWidget){
					String displayStr = ((SystemManagedColWidget)col).createDisplayText(dataList.get(i)).getText();
					Integer occurances = map.get(displayStr);
					if (occurances == null)
						occurances = 1;
					else 
						occurances++;
					map.put(displayStr, occurances);
				}*/
			}
			/*Set<String> displayStrSet = map.keySet();
			Iterator<String> itt = displayStrSet.iterator();
			List<FilterMenuItem> lst = new ArrayList<FilterMenuItem>();
			while(itt.hasNext()){
				String item = itt.next();
				FilterMenuItem obj = new FilterMenuItem(item, item + "(" + map.get(item) +")", false);
				lst.add(obj);
			}*/
			
			List/*<FilterItemWithOccurance>*/ retLst = Arrays.asList(map.values().toArray());
			filterListCallBack.onFetchComplete(retLst);
//		}
	}

//	private Map<ColWidget, List<FilterMenuItem>> filterMenus; 
	private List<VO> createFilteredList(Map<ColWidget, List<FilterMenuItem>> filters, List<VO> listToFilter){
		if (filters == null || filters.size() == 0) 
			return listToFilter;
//		if (filterMenus == null)
//			filterMenus = new HashMap<ColWidget, List<FilterMenuItem>>();
		Set<ColWidget> filterCols = filters.keySet();
		List<VO> retList = dataList;
		for (Iterator<ColWidget> iterator = filterCols.iterator(); iterator.hasNext();) {
			ColWidget col = (ColWidget) iterator.next();
			HasComparable<VO> comparableCol;
			if (col instanceof HasComparable) {
				comparableCol = (HasComparable<VO>) col;
			}else break;
			List<FilterMenuItem> colFilterLst = filters.get(col);
//			filterMenus.put(col, colFilterLst);
			retList = createMatchingVos(retList, comparableCol, colFilterLst);
			
//			int colFilterSize = colFilterLst.size();
//			for (int i = 0; i < colFilterSize; i++) {
//				FilterMenuItem colFilter = colFilterLst.get(i);
//				if (colFilter.isChecked()){
////					String colFilterValue = colFilter.getValue();
//					retList = createMatchingVos(retList, comparableCol, colFilter.getValue());
////					comparableCol.getComparableValue(colFilterValue)
//				}
//				
//			}
		}
		
		return retList;
	}
	
	private List<VO> createMatchingVos(List<VO> listToFilter, HasComparable<VO> comparableCol,
			List<FilterMenuItem> colFilterLst) {
		int dataListSize = listToFilter.size();
		List<VO> retVos = new ArrayList<VO>();
		
		int colFilterSize = colFilterLst.size();
		String filterVal;
		FilterMenuItem colFilterItem;
		for (int i=0; i<dataListSize; i++){
			VO vo = listToFilter.get(i);
			Comparable comparableVal = comparableCol.getComparableValue(vo);
			for (int j = 0; j < colFilterSize; j++){
				colFilterItem = colFilterLst.get(j);
				filterVal = colFilterItem.getValue();
				if (colFilterItem.isChecked() && filterVal.toString().equals(comparableVal.toString())){
					retVos.add(vo);
					break;
				}
			}
				
		}
		return retVos;
	}
//	private List<VO> createMatchingVos(List<VO> listToFilter, HasComparable<VO> comparableCol,
//			String filterVal) {
//		int dataListSize = listToFilter.size();
//		List<VO> retVos = new ArrayList<VO>();
//		for (int i=0; i<dataListSize; i++){
//			VO vo = listToFilter.get(i);
//			Comparable comparableVal = comparableCol.getComparableValue(vo);
//			if (comparableVal.equals(filterVal))
//				retVos.add(vo);
//		}
//		return retVos;
//	}

	private List<VO> createFilteredList1(Map<ColWidget, List<FilterMenuItem>> filters, List<VO> listToFilter){
		if (filters == null || filters.size() == 0)
			return listToFilter;
		List<VO> retList = new ArrayList<VO>();
		int lstToFilterSize = listToFilter.size();
		Set<ColWidget> filterCols;
		Iterator<ColWidget> filterColsItt;
		ColWidget filterColW;
		boolean filterMatch;
		String voVal;
		List<FilterMenuItem> filterVals;
		FilterMenuItem filterVal;
		int filterValsSize; 
		for (int i=0; i < lstToFilterSize; i++){
			VO vo = listToFilter.get(i);
			filterCols = filters.keySet();
			filterColsItt = filterCols.iterator();
			filterMatch = true; 
			
			while(filterColsItt.hasNext()){
				filterColW = filterColsItt.next();
				if (filterColW instanceof HasComparable){
					filterVals = filters.get(filterColW);
					filterValsSize = filterVals.size(); 
					voVal = ((HasComparable)filterColW).getComparableValue(dataList.get(i)).toString();
					boolean passFilterValCheck = false; 
					for (int iVals = 0; iVals < filterValsSize; iVals++){
						filterVal = filterVals.get(iVals);
						if (filterVal.getValue().equals(voVal)){
							passFilterValCheck = true;
							break;
						}
					}
					if (!passFilterValCheck){
						filterMatch = false; 
						break;
					}
				}
			}
			if (filterMatch)
				retList.add(vo);
		}
		return retList;
	}
	
	@Override
	public void fetchRowData(int startRow, int endRow, ColWidget sortCol,
			Boolean sortColAsc, Map<ColWidget, List<FilterMenuItem>> filters, FetchCompleteCallBack<VO> fetchCompleteCallBack) {
		List<VO> filteredLst = createFilteredList(filters, dataList);
		this.filteredList = filteredLst;
		if (sortCol != null)
			sortList(filteredLst, sortCol, sortColAsc);
		List<VO> retLst = new ArrayList<VO>();
		int filteredLstSize = filteredLst.size();
		int newEndRow = filteredLstSize < endRow?filteredLstSize: endRow; 
		for (int i=startRow; i<newEndRow; i++)
			retLst.add(filteredLst.get(i));
		fetchCompleteCallBack.onFetchComplete(retLst);
	}

	private void sortList(List<VO> listToSort, final ColWidget sortCol, final Boolean sortColAsc) {
		Collections.sort(listToSort, new Comparator<VO>(){

			public int compare(VO o1, VO o2) {
				Comparable compare1 = ((HasComparable<VO>)sortCol).getComparableValue(o1);
				Comparable compare2 = ((HasComparable<VO>)sortCol).getComparableValue(o2);
				if (sortColAsc)
					return compare1.compareTo(compare2);
				else 
					return compare2.compareTo(compare1);
			}			
		});
	}

}
